/**
 * 
 * 自定义Promise模块
 */
(function(window) {
  const PENDING = 'pending' 
  const RESOLVED = 'resolved'
  const REJECTED = 'rejected'

  class Promise {
    // Promise构造函数
    constructor(excutor) {
      const self = this 
      self.status = PENDING // 给promise对象指定一个存放状态的属性 初始状态为pending
      self.data = undefined // 给promise对象指定一个用于存储结果数据的属性
      self.callbacks = [] // 每个元素的结构({onResolved, onRejected})

      // 定义resolve函数
      function resolve(value) {
        // 判断如果值不为pending,直接结束
        if(self.status !== PENDING) return

        self.status = RESOLVED //调用resolve时,把状态改为resolved
        self.data = value // 把value数据保存在data中

        // 如果callbacks里有值,立刻异步执行onResolved
        if(self.callbacks.length > 0) {
          // 放入队列中执行所有成功回调
          setTimeout(() => {
            self.callbacks.forEach(cbsObj => {
              cbsObj.onResolved(value)
            })
          })
        }
      }

      // 定义reject函数
      function reject(reason) {
         // 判断如果值不为pending,直接结束
         if(self.status !== PENDING) return

        self.status = REJECTED // 调用reject时,把状态改为rejected
        self.data = reason // 把reason数据保存在data中

        // 如果callbacks里有值,立刻异步执行onRejected
        if(self.callbacks.length > 0) {
          // 放入队列中执行所有失败回调
          setTimeout(() => {
            self.callbacks.forEach(cbsObj => {
              cbsObj.onRejected(reason)
            })
          })
        }
      }

      // 立即同步执行excutor,两个参数均为函数
      excutor(resolve, reject)
    }

    /*
    用来指定成功/失败回调函数的方法
      1. 如果当前promise为resolved,异步执行成功的回调函数onResolved
      2. 如果当前promise为rejected,异步执行失败的回调函数onRejected
      3. 如果当前promise为pending,保存回调函数
    返回一个新的promise对象
      它的结果状态由onResolved和onRejected执行的结果决定
      1. 抛出error ==> 变为rejected, 结果值为error
      2. 返回值不是promise ==> 变为resolved, 结果值为返回值
      3. 返回值是promise ==> 由这个promise的值决定新的promise的结果(成功/失败)
    */
    then (onResolved, onRejected) {
      const self = this // 保存promise实例对象
    
      // 将value向下传递
      onResolved = typeof onResolved === 'function' ? onResolved : value => value
      // 将reason向下传递,错误穿透
      onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason}
    
      // 返回新的promise对象
      return new Promise((resolve, reject) => {
      
        // 封装一个保存公共代码的函数
        function handle(callback) {
          try {
            const result = callback(self.data)
            // 2.2 结果值如果为promise,由这个promise的值决定新的promise的结果(成功/失败)
            if(result instanceof Promise) {
              result.then(
                value => resolve(value),
                reason => reject(reason)
              )
            // 2.3 结果值不为promise,状态改变为resolved,返回这个结果值
            } else {
              resolve(result)
            }
          } catch (error) {
            // 2.1 如果抛出error,状态变为rejected,结果值为error
            reject(error)
          }
        }
      
        // 1. 如果当前promise为resolved,异步执行成功的回调函数onResolved
        if(self.status === RESOLVED) {
          setTimeout(() => {
            handle(onResolved)
          })
        
        // 2. 如果当前promise为rejected,异步执行失败的回调函数onRejected 
        } else if(self.status === REJECTED) {
          setTimeout(() => {
            handle(onRejected)
          })
        
        // 3. 如果当前promise为pending,保存回调函数
        } else {
          self.callbacks.push({
            onResolved() {
              handle(onResolved)
            },
            onRejected() {
              handle(onRejected)
            }
          })
        }
      })
    }
  
    // 用来指定失败回调函数的方法,返回一个新的promise对象
    catch (onRejected) {
      // this为Promise实例,实例上可以使用原型上的方法
      return this.then(undefined, onRejected)
    }

      /*
      用来返回一个指定value的成功的promise
      value可能是一个一般值,也可能是Promise对象
      */
      static resolve = function(value) {
        return new Promise((resolve, reject) => {
          // value是promise
          if(value instanceof Promise) { // 使用value的结果作为promise的结果
            // value.then(
            //   val => {resolve(val)},
            //   reason => {reject(reason)}
            // )
            // 简写
            value.then(resolve, reject)
          // value不是promise,把状态改为resolved,数据是value
          } else {
            resolve(value)
          }
        })
      }
    
      // 用来返回一个指定reason的失败的promise
      static reject = function(reason) {
        // 返回一个失败的promise
        return new Promise((resolve, reject) => {
          reject(reason)
        })
      }
    
      // 返回一个新的promise对象,只有promises中所有的promise都产生成功value时才成功,否则失败
      static all = function(promises) {
        const values = new Array(promises.length) // 用于保存所有成功value的数组
        let valuesCount = 0 // 记录promises中成功的个数
        return new Promise((resolve, reject) => {
          promises.forEach((p, index) => {
            p.then(
              value => {
                valuesCount++
                // p成功,将value存到values数组中
                values[index] = value
                // 如果valuesCount的值等于promises的个数,则表示都成功
                if(valuesCount === promises.length) {
                  resolve(values)
                }
              }, 
              reason => reject(reason) //一旦有失败,将return变为失败
            )
          })
        })
      }
    
      // 返回一个新的promise对象,由第一个完成的promise决定
      static race = function(promises) {
        return new Promise((resolve, reject) => {
          // 遍历所有promise,取对应结果
          promises.forEach(p => {
            p.then(
              value => resolve(value), // 一旦有成功,将return变为成功
              reason => reject(reason) // 如果有失败,将return变为失败
            )
          })
        })
      }
    
      // 返回一个延迟指定时间才成功的promise
      static resolveDelay = function(value, time) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            // 如果value是promise
            if(value instanceof Promise) {
              value.then(
                value => resolve(value),
                reason => reject(reason)
              )
            // 如果value不是promise
            } else {
              resolve(value)
            }
          }, time)
        })
      }
    
      // 返回一个延迟指定时间才失败的promise
      static rejectDelay = function(reason, time) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            reject(reason)
          }, time)
        })
      }
  }

  // 向外暴露Promise
  window.Promise = Promise

})(window)